// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "main.h"
#include "synchconsole.h"
#include "syscall.h"
#include "ksyscall.h"
//----------------------------------------------------------------------
// ExceptionHandler
// 	Entry point into the Nachos kernel.  Called when a user program
//	is executing, and either does a syscall, or generates an addressing
//	or arithmetic exception.
//
// 	For system calls, the following is the calling convention:
//
// 	system call code -- r2
//		arg1 -- r4
//		arg2 -- r5
//		arg3 -- r6
//		arg4 -- r7
//
//	The result of the system call, if any, must be put back into r2. 
//
// If you are handling a system call, don't forget to increment the pc
// before returning. (Or else you'll loop making the same system call forever!)
//
//	"which" is the kind of exception.  The list of possible exceptions 
//	is in machine.h.
//----------------------------------------------------------------------

void ModifyRegister()
{
	kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
	kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
	kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4);
}


void CopyToUser(char *FromKernelAddress, int NumBytes, int ToUserAddress)
{
	int i;
	for (i=0; i<NumBytes; i++)
	{
		kernel->machine->WriteMem(ToUserAddress, 1, (int)*FromKernelAddress );
		FromKernelAddress++;
		ToUserAddress++;
	}
}

void CopyToKernel(int FromUserAddress, int NumBytes, char *ToKernelAddress)
{
	int i, c;
	for (i=0; i<NumBytes; i++)
	{
		kernel->machine->ReadMem(FromUserAddress++, 1, &c);
		*ToKernelAddress++ = (char)c;
	}
}

void
ExceptionHandler(ExceptionType which)
{
    int type = kernel->machine->ReadRegister(2);

    DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n");


    switch (which)
    {
		case SyscallException:
		  switch(type) {
		  case SC_Halt:
		DEBUG(dbgSys, "Shutdown, initiated by user program.\n");

		SysHalt();

		ASSERTNOTREACHED();
		break;
	
	


		case SC_Print:
		{
			DEBUG(dbgGxc,"step9---in print\n");
			DEBUG( dbgSys, "Print" << kernel->machine->ReadRegister(2) << "<<\n");
			int mem,value;
			mem = kernel->machine->ReadRegister(4);
			do
			{
				if(kernel->machine->ReadMem(mem,1,&value) == FALSE)
					break;
				kernel->synchConsoleOut->PutChar(char(value));
				mem++;
			}while(value != '\0');

			ModifyRegister();

			return;
			ASSERTNOTREACHED();
			break;
		}


		 case SC_PrintNum:
		 {
			DEBUG( dbgSys, "PrintNum" << kernel->machine->ReadRegister(4) << "<<\n");
			int mem2,value2;
			//mem2 = kernel->machine->ReadRegister(4);
			//kernel->machine->ReadMem(mem,1,&value2);
			cout<<kernel->machine->ReadRegister(4)<<endl;

			ModifyRegister();

			return;
			ASSERTNOTREACHED();
		    break;
		 }

		 case SC_Exec:
		 {
			DEBUG(dbgGxc,"step6---in SC_Exec\n");
			DEBUG( dbgSys, "Exec" << kernel->machine->ReadRegister(4) << "<<\n");
			int mem3,i;
			AddrSpace *space;
			char filename[20];

			mem3 = kernel->machine->ReadRegister(4);
//			cout << "parameter is " << mem3 << endl;
			for (i=0; i<5; i++)
			{
				kernel->machine->ReadMem(mem3+4*i,4,(int *)(filename+4*i));
			}
//			cout << "Executable file is "<<filename<<endl;

			ModifyRegister();
			//StartProcess(filename);

			kernel->lstaddrspace->Front()->SaveReg();
			space = new AddrSpace(kernel->fileSystem->Open(filename));
			kernel->currentThread->space = space;
			space->Execute();
			delete space;
			return;
			ASSERTNOTREACHED();
		    break;
		 }
		 case SC_Write:
		 {
			//void Write(char *buffer, int size, OpenFileId id);
			char *buffer;
			int bufferVrtAddr = kernel->machine->ReadRegister(4); // 1st parameter
			int size = kernel->machine->ReadRegister(5);    // 2nd parameter
			int fd = kernel->machine->ReadRegister(6);		// 3rd parameter

			buffer = new char[size];
			CopyToKernel(bufferVrtAddr, size, buffer);
			if (fd == ConsoleOutput)
			{ //stdout
				for (int i=0;i<size;i++)
					kernel->synchConsoleOut->PutChar(buffer[i]);
			}
			delete buffer;

			ModifyRegister();

			return;
			ASSERTNOTREACHED();
			break;
		 }

		 case SC_Read:
		 {
			 //int Read(char *buffer, int size, OpenFileId id);
			char *buffer;
			int bufferVrtAddr = kernel->machine->ReadRegister(4); // 1st parameter
			int size = kernel->machine->ReadRegister(5);  // 2nd parameter
			int fd = kernel->machine->ReadRegister(6);    // 3rd parameter

			buffer = new char[size];
			if (fd == ConsoleInput)
			{  //stdin
				for (int i=0;i<size;i++)
					buffer[i] = kernel->synchConsoleIn->GetChar();
				CopyToUser(buffer, size, bufferVrtAddr);
			}
			delete buffer;

			ModifyRegister();

			return;
			ASSERTNOTREACHED();
			break;
		 }

		  case SC_Add:
		  {
				DEBUG(dbgSys, "Add " << kernel->machine->ReadRegister(4) << " + " << kernel->machine->ReadRegister(5) << "\n");

				/* Process SysAdd Systemcall*/
				int result;
				result = SysAdd(/* int op1 */(int)kernel->machine->ReadRegister(4),
						/* int op2 */(int)kernel->machine->ReadRegister(5));

				DEBUG(dbgSys, "Add returning with " << result << "\n");
				/* Prepare Result */
				kernel->machine->WriteRegister(2, (int)result);

				/* Modify return point */
				ModifyRegister();;

				return;
				ASSERTNOTREACHED();
				break;
		  }

		  case SC_Join:
		  {
			  DEBUG(dbgSys,"join "<<kernel->machine->ReadRegister(4)<<"\n");

		  }


		  case SC_Exit:
		  {
			  DEBUG(dbgGxc,"in Exit()\n");

			  AddrSpace *space = kernel->lstaddrspace->RemoveFront();
			  delete space;

			  if (kernel->lstaddrspace->IsEmpty())
			  {
				  DEBUG(dbgGxc,"in Exit()---lstaddrspace empty\n");
				  SysHalt();
			  }
			  space = kernel->lstaddrspace->Front();
			  kernel->currentThread->space = space;

			  ModifyRegister();
			  space->RestoreState();
			  space->RestoreReg();		// load page table register


			  kernel->machine->Run();		// jump to the user progam

			  return;
			  ASSERTNOTREACHED();
			  break;
		  }

		  default:
			  cerr << "Unexpected system call " << type << "\n";
			  break;
      }
      break;
    default:
      cerr << "Unexpected user mode exception" << (int)which << "\n";
      break;
    }
    ASSERTNOTREACHED();
}
